home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Image / GraphViz.php < prev    next >
Encoding:
PHP Script  |  2005-07-07  |  14.0 KB  |  507 lines

  1. <?php
  2. //
  3. // +---------------------------------------------------------------------------+
  4. // | PEAR :: Image :: GraphViz                                                 |
  5. // +---------------------------------------------------------------------------+
  6. // | Copyright (c) 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de> and |
  7. // |                         Dr. Volker G÷bbels <vmg@arachnion.de>.            |
  8. // +---------------------------------------------------------------------------+
  9. // | This source file is subject to version 3.00 of the PHP License,           |
  10. // | that is available at http://www.php.net/license/3_0.txt.                  |
  11. // | If you did not receive a copy of the PHP license and are unable to        |
  12. // | obtain it through the world-wide-web, please send a note to               |
  13. // | license@php.net so we can mail you a copy immediately.                    |
  14. // +---------------------------------------------------------------------------+
  15. //
  16. // $Id: GraphViz.php,v 1.23 2004/12/22 08:06:11 sebastian Exp $
  17. //
  18.  
  19. require_once 'System.php';
  20.  
  21. /**
  22.  * Interface to AT&T's GraphViz tools.
  23.  *
  24.  * The GraphViz class allows for the creation of and the work with directed
  25.  * and undirected graphs and their visualization with AT&T's GraphViz tools.
  26.  *
  27.  * <code>
  28.  * <?php
  29.  * require_once 'Image/GraphViz.php';
  30.  *
  31.  * $graph = new Image_GraphViz();
  32.  *
  33.  * $graph->addNode(
  34.  *   'Node1',
  35.  *   array(
  36.  *     'URL'   => 'http://link1',
  37.  *     'label' => 'This is a label',
  38.  *     'shape' => 'box'
  39.  *   )
  40.  * );
  41.  *
  42.  * $graph->addNode(
  43.  *   'Node2',
  44.  *   array(
  45.  *     'URL'      => 'http://link2',
  46.  *     'fontsize' => '14'
  47.  *   )
  48.  * );
  49.  *
  50.  * $graph->addNode(
  51.  *   'Node3',
  52.  *   array(
  53.  *     'URL'      => 'http://link3',
  54.  *     'fontsize' => '20'
  55.  *   )
  56.  * );
  57.  *
  58.  * $graph->addEdge(
  59.  *   array(
  60.  *     'Node1' => 'Node2'
  61.  *   ),
  62.  *   array(
  63.  *     'label' => 'Edge Label'
  64.  *   )
  65.  * );
  66.  *
  67.  * $graph->addEdge(
  68.  *   array(
  69.  *     'Node1' => 'Node2'
  70.  *   ),
  71.  *   array(
  72.  *     'color' => 'red'
  73.  *   )
  74.  * );
  75.  *
  76.  * $graph->image();
  77.  * ?>
  78.  * </code>
  79.  *
  80.  * @author    Sebastian Bergmann <sb@sebastian-bergmann.de>
  81.  * @author    Dr. Volker G÷bbels <vmg@arachnion.de>
  82.  * @author    Karsten Dambekalns <k.dambekalns@fishfarm.de>
  83.  * @copyright Copyright © 2002-2005 Sebastian Bergmann <sb@sebastian-bergmann.de> and Dr. Volker G÷bbels <vmg@arachnion.de>
  84.  * @license   http://www.php.net/license/3_0.txt The PHP License, Version 3.0
  85.  * @category  Image
  86.  * @package   Image_GraphViz
  87.  */
  88. class Image_GraphViz {
  89.     /**
  90.     * Path to GraphViz/dot command
  91.     *
  92.     * @var  string
  93.     */
  94.     var $dotCommand = 'dot';
  95.  
  96.     /**
  97.     * Path to GraphViz/neato command
  98.     *
  99.     * @var  string
  100.     */
  101.     var $neatoCommand = 'neato';
  102.  
  103.     /**
  104.     * Representation of the graph
  105.     *
  106.     * @var  array
  107.     */
  108.     var $graph;
  109.  
  110.     /**
  111.     * Constructor.
  112.     *
  113.     * Setting the name of the Graph is useful for including multiple image maps on
  114.     * one page. If not set, the graph will be named 'G'.
  115.     *
  116.     * @param  boolean $directed Directed (TRUE) or undirected (FALSE) graph.
  117.     * @param  array   $attributes Attributes of the graph
  118.     * @param  string  $name Name of the Graph
  119.     * @access public
  120.     */
  121.     function Image_GraphViz($directed = TRUE, $attributes = array(), $name = NULL) {
  122.         $this->setDirected($directed);
  123.         $this->setAttributes($attributes);
  124.         $this->graph['name'] = $name;
  125.     }
  126.  
  127.     /**
  128.     * Output image of the graph in a given format.
  129.     *
  130.     * @param  string  Format of the output image.
  131.     *                 This may be one of the formats supported by GraphViz.
  132.     * @access public
  133.     */
  134.     function image($format = 'svg') {
  135.         if ($data = $this->fetch($format)) {
  136.             $sendContentLengthHeader = TRUE;
  137.  
  138.             switch ($format) {
  139.                 case 'gif':
  140.                 case 'png':
  141.                 case 'wbmp': {
  142.                     header('Content-Type: image/' . $format);
  143.                 }
  144.                 break;
  145.  
  146.                 case 'jpg': {
  147.                     header('Content-Type: image/jpeg');
  148.                 }
  149.                 break;
  150.  
  151.                 case 'pdf': {
  152.                     header('Content-Type: application/pdf');
  153.                 }
  154.                 break;
  155.  
  156.                 case 'svg': {
  157.                     header('Content-Type: image/svg+xml');
  158.                 }
  159.                 break;
  160.  
  161.                 default: {
  162.                     $sendContentLengthHeader = FALSE;
  163.                 }
  164.             }
  165.  
  166.             if ($sendContentLengthHeader) {
  167.                 header('Content-Length: ' . strlen($data));
  168.             }
  169.  
  170.             echo $data;
  171.         }
  172.     }
  173.  
  174.     /**
  175.     * Return image (data) of the graph in a given format.
  176.     *
  177.     * @param  string  Format of the output image.
  178.     *                 This may be one of the formats supported by GraphViz.
  179.     * @return string  The image (data) created by GraphViz.
  180.     * @access public
  181.     * @since  1.1.0
  182.     */
  183.     function fetch($format = 'svg') {
  184.         if ($file = $this->saveParsedGraph()) {
  185.             $outputfile = $file . '.' . $format;
  186.             $command  = $this->graph['directed'] ? $this->dotCommand : $this->neatoCommand;
  187.             $command .= " -T$format -o$outputfile $file";
  188.     
  189.             @`$command`;
  190.             @unlink($file);
  191.     
  192.             $fp = fopen($outputfile, 'rb');
  193.     
  194.             if ($fp) {
  195.                 $data = fread($fp, filesize($outputfile));
  196.                 fclose($fp);
  197.                 @unlink($outputfile);
  198.             }
  199.     
  200.             return $data;
  201.         }
  202.     
  203.         return FALSE;
  204.     }
  205.  
  206.     /**
  207.     * Add a cluster to the graph.
  208.     *
  209.     * @param  string  ID.
  210.     * @param  array   Title.
  211.     * @param  array   Attributes of the cluster.
  212.     * @access public
  213.     */
  214.     function addCluster($id, $title, $attributes = array()) {
  215.         $this->graph['clusters'][$id]['title'] = $title;
  216.         $this->graph['clusters'][$id]['attributes'] = $attributes;
  217.     }
  218.  
  219.     /**
  220.     * Add a note to the graph.
  221.     *
  222.     * @param  string  Name of the node.
  223.     * @param  array   Attributes of the node.
  224.     * @param  string  Group of the node.
  225.     * @access public
  226.     */
  227.     function addNode($name, $attributes = array(), $group = 'default') {
  228.         $this->graph['nodes'][$group][$name] = $attributes;
  229.     }
  230.  
  231.     /**
  232.     * Remove a node from the graph.
  233.     *
  234.     * @param  Name of the node to be removed.
  235.     * @access public
  236.     */
  237.     function removeNode($name, $group = 'default') {
  238.         if (isset($this->graph['nodes'][$group][$name])) {
  239.             unset($this->graph['nodes'][$group][$name]);
  240.         }
  241.     }
  242.  
  243.     /**
  244.     * Add an edge to the graph.
  245.     *
  246.     * Caveat! This cannot handle multiple identical edges. If you use non-numeric
  247.     * IDs for the nodes, this will not do (too much) harm. For numeric IDs the
  248.     * array_merge() that is used will change the keys when merging arrays, leading
  249.     * to new nodes appearing in the graph.
  250.     *
  251.     * @param  array Start and End node of the edge.
  252.     * @param  array Attributes of the edge.
  253.     * @access public
  254.     */
  255.     function addEdge($edge, $attributes = array()) {
  256.         if (is_array($edge)) {
  257.             $from = key($edge);
  258.             $to   = $edge[$from];
  259.             $id   = $from . '_' . $to;
  260.  
  261.             if (!isset($this->graph['edges'][$id])) {
  262.                 $this->graph['edges'][$id] = $edge;
  263.             } else {
  264.                 $this->graph['edges'][$id] = array_merge(
  265.                   $this->graph['edges'][$id],
  266.                   $edge
  267.                 );
  268.             }
  269.  
  270.             if (is_array($attributes)) {
  271.                 if (!isset($this->graph['edgeAttributes'][$id])) {
  272.                     $this->graph['edgeAttributes'][$id] = $attributes;
  273.                 } else {
  274.                     $this->graph['edgeAttributes'][$id] = array_merge(
  275.                       $this->graph['edgeAttributes'][$id],
  276.                       $attributes
  277.                     );
  278.                 }
  279.             }
  280.         }
  281.     }
  282.  
  283.     /**
  284.     * Remove an edge from the graph.
  285.     *
  286.     * @param  array Start and End node of the edge to be removed.
  287.     * @access public
  288.     */
  289.     function removeEdge($edge) {
  290.         if (is_array($edge)) {
  291.               $from = key($edge);
  292.               $to   = $edge[$from];
  293.               $id   = $from . '_' . $to;
  294.  
  295.             if (isset($this->graph['edges'][$id])) {
  296.                 unset($this->graph['edges'][$id]);
  297.             }
  298.  
  299.             if (isset($this->graph['edgeAttributes'][$id])) {
  300.                 unset($this->graph['edgeAttributes'][$id]);
  301.             }
  302.         }
  303.     }
  304.  
  305.     /**
  306.     * Add attributes to the graph.
  307.     *
  308.     * @param  array Attributes to be added to the graph.
  309.     * @access public
  310.     */
  311.     function addAttributes($attributes) {
  312.         if (is_array($attributes)) {
  313.             $this->graph['attributes'] = array_merge(
  314.               $this->graph['attributes'],
  315.               $attributes
  316.             );
  317.         }
  318.     }
  319.  
  320.     /**
  321.     * Set attributes of the graph.
  322.     *
  323.     * @param  array Attributes to be set for the graph.
  324.     * @access public
  325.     */
  326.     function setAttributes($attributes) {
  327.         if (is_array($attributes)) {
  328.             $this->graph['attributes'] = $attributes;
  329.         }
  330.     }
  331.  
  332.     /**
  333.     * Set directed/undirected flag for the graph.
  334.     *
  335.     * @param  boolean Directed (TRUE) or undirected (FALSE) graph.
  336.     * @access public
  337.     */
  338.     function setDirected($directed) {
  339.         if (is_bool($directed)) {
  340.             $this->graph['directed'] = $directed;
  341.         }
  342.     }
  343.  
  344.     /**
  345.     * Load graph from file.
  346.     *
  347.     * @param  string  File to load graph from.
  348.     * @access public
  349.     */
  350.     function load($file) {
  351.         if ($serialized_graph = implode('', @file($file))) {
  352.             $this->graph = unserialize($serialized_graph);
  353.         }
  354.     }
  355.  
  356.     /**
  357.     * Save graph to file.
  358.     *
  359.     * @param  string  File to save the graph to.
  360.     * @return mixed   File the graph was saved to, FALSE on failure.
  361.     * @access public
  362.     */
  363.     function save($file = '') {
  364.         $serialized_graph = serialize($this->graph);
  365.  
  366.         if (empty($file)) {
  367.             $file = System::mktemp('graph_');
  368.         }
  369.  
  370.         if ($fp = @fopen($file, 'w')) {
  371.             @fputs($fp, $serialized_graph);
  372.             @fclose($fp);
  373.  
  374.             return $file;
  375.         }
  376.  
  377.         return FALSE;
  378.     }
  379.  
  380.     /**
  381.     * Parse the graph into GraphViz markup.
  382.     *
  383.     * @return string  GraphViz markup
  384.     * @access public
  385.     */
  386.     function parse() {
  387.         if (isset($this->graph['name']) && is_string($this->graph['name'])) {
  388.             $parsedGraph = "digraph " . $this->graph['name'] . " {\n";
  389.         } else {
  390.             $parsedGraph = "digraph G {\n";
  391.         }
  392.  
  393.         if (isset($this->graph['attributes'])) {
  394.             foreach ($this->graph['attributes'] as $key => $value) {
  395.                 $attributeList[] = $key . '="' . $value . '"';
  396.             }
  397.  
  398.             if (!empty($attributeList)) {
  399.                 $parsedGraph .= 'graph [ '.implode(',', $attributeList) . " ];\n";
  400.             }
  401.         }
  402.  
  403.         if (isset($this->graph['nodes'])) {
  404.             foreach($this->graph['nodes'] as $group => $nodes) {
  405.                 if ($group != 'default') {
  406.                     $parsedGraph .= sprintf(
  407.                       "subgraph \"cluster_%s\" {\nlabel=\"%s\";\n",
  408.  
  409.                       $group,
  410.                       isset($this->graph['clusters'][$group]) ? $this->graph['clusters'][$group]['title'] : ''
  411.                     );
  412.  
  413.                     if (isset($this->graph['clusters'][$group]['attributes'])) {
  414.                         unset($attributeList);
  415.  
  416.                         foreach ($this->graph['clusters'][$group]['attributes'] as $key => $value) {
  417.                             $attributeList[] = $key . '="' . $value . '"';
  418.                         }
  419.  
  420.                         if (!empty($attributeList)) {
  421.                             $parsedGraph .= implode(',', $attributeList) . ";\n";
  422.                         }
  423.                     }
  424.                 }
  425.  
  426.                 foreach($nodes as $node => $attributes) {
  427.                     unset($attributeList);
  428.  
  429.                     foreach($attributes as $key => $value) {
  430.                         $attributeList[] = $key . '="' . $value . '"';
  431.                     }
  432.  
  433.                     if (!empty($attributeList)) {
  434.                         $parsedGraph .= sprintf(
  435.                           "\"%s\" [ %s ];\n",
  436.                           addslashes(stripslashes($node)),
  437.                           implode(',', $attributeList)
  438.                         );
  439.                     }
  440.                 }
  441.  
  442.                 if ($group != 'default') {
  443.                   $parsedGraph .= "}\n";
  444.                 }
  445.             }
  446.         }
  447.  
  448.         if (isset($this->graph['edges'])) {
  449.             foreach($this->graph['edges'] as $label => $node) {
  450.                 unset($attributeList);
  451.  
  452.                 $from = key($node);
  453.                 $to   = $node[$from];
  454.  
  455.                 foreach($this->graph['edgeAttributes'][$label] as $key => $value) {
  456.                     $attributeList[] = $key . '="' . $value . '"';
  457.                 }
  458.  
  459.                 $parsedGraph .= sprintf(
  460.                   '"%s" -> "%s"',
  461.                   addslashes(stripslashes($from)),
  462.                   addslashes(stripslashes($to))
  463.                 );
  464.                 
  465.                 if (!empty($attributeList)) {
  466.                     $parsedGraph .= sprintf(
  467.                       ' [ %s ]',
  468.                       implode(',', $attributeList)
  469.                     );
  470.                 }
  471.  
  472.                 $parsedGraph .= ";\n";
  473.             }
  474.         }
  475.  
  476.         return $parsedGraph . "}\n";
  477.     }
  478.  
  479.     /**
  480.     * Save GraphViz markup to file.
  481.     *
  482.     * @param  string  File to write the GraphViz markup to.
  483.     * @return mixed   File to which the GraphViz markup was
  484.     *                 written, FALSE on failure.
  485.     * @access public
  486.     */
  487.     function saveParsedGraph($file = '') {
  488.         $parsedGraph = $this->parse();
  489.  
  490.         if (!empty($parsedGraph)) {
  491.             if (empty($file)) {
  492.                 $file = System::mktemp('graph_');
  493.             }
  494.  
  495.             if ($fp = @fopen($file, 'w')) {
  496.                 @fputs($fp, $parsedGraph);
  497.                 @fclose($fp);
  498.  
  499.                 return $file;
  500.             }
  501.         }
  502.  
  503.         return FALSE;
  504.     }
  505. }
  506. ?>
  507.